#include "CMemoryManager.h"
#include <iomanip>

namespace common {

cEXPORT CMemoryManager g_memoryManager;

//------------------------------------------------------------
const char*    CMemoryManager::s_pOutputFilePath = "memory.log";

//------------------------------------------------------------
CMemoryManager::CMemoryManager()
{
  //set_new_handler();
  m_OutputFile.open( s_pOutputFilePath, ios_base::out );
#ifdef _MSC_VER // Visual studio
  // Memory leaks report
  _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif
}

//------------------------------------------------------------
CMemoryManager::~CMemoryManager()
{
  if( !m_Blocks.empty() )
  {
    //m_File << " Oops... Some leaks have been detected " << std::endl;
    reportLeaks();
  }
}

//------------------------------------------------------------
/*CMemoryManager& CMemoryManager::instance()
{
	static CMemoryManager inst;
	return inst;
}*/

//------------------------------------------------------------
void cCALL CMemoryManager::reportLeaks()
{
  size_t    iTotalSize = 0;
  for (CBlockMap::iterator i = m_Blocks.begin(); i != m_Blocks.end(); ++i)
  {
    iTotalSize += i->second.m_iSize;
    m_OutputFile << "-> 0x" << i->first
      << " | " << std::setw(7) << std::setfill(' ')
      << static_cast<int>(i->second.m_iSize) << " bytes"
      << " | " << i->second.m_sFile
      << " (" << i->second.m_iLine << ")" << std::endl;
    free(i->first);
  }
  m_OutputFile << std::endl << std::endl << "-- "
    << static_cast<int>(m_Blocks.size()) << " blocks that are not freed, "
    << static_cast<int>(iTotalSize) << " bytes --"
    << std::endl;
}

//------------------------------------------------------------
void cCALL CMemoryManager::nextDelete(const string& sFile, int iLine)
{
  CBlock nextDeletedBlock;
  nextDeletedBlock.m_sFile = sFile;
  nextDeletedBlock.m_iLine = iLine;
  m_DeleteStack.push( nextDeletedBlock );
}

//------------------------------------------------------------
void* cCALL CMemoryManager::_alloc(size_t iSize, const string& sFile, int iLine, bool
                               bIsArray)
{
  void* pPtr = malloc(iSize);
  CBlock newBlock;
  newBlock.m_iSize      = iSize;
  newBlock.m_sFile      = sFile;
  newBlock.m_iLine      = iLine;
  newBlock.m_bIsArray   = bIsArray;
  m_Blocks[ pPtr ] = newBlock;

  m_OutputFile << "++ Allocation | 0x" << pPtr
    << " | " << std::setw(7) << std::setfill(' ')
    << static_cast<int>(newBlock.m_iSize) << " bytes"
    << " | " << newBlock.m_sFile
    << "(" << newBlock.m_iLine << ")" << std::endl;
  return pPtr;
}

//------------------------------------------------------------
void cCALL CMemoryManager::_free(void* pPtr, bool bIsArray)
{
  CBlockMap::iterator it = m_Blocks.find( pPtr );
  if( it == m_Blocks.end() )
  {
    free(pPtr);
    return;
  }
  if (it->second.m_bIsArray != bIsArray)
  {
    //throw CBadDelete(Ptr, It->second.File.Filename(), It->second.Line, !Array);
  }
  m_OutputFile << "-- Deallocation | 0x" << pPtr
    << " | " << std::setw(7) << std::setfill(' ')
    << static_cast<int>(it->second.m_iSize) << " bytes"
    << " | " << m_DeleteStack.top().m_sFile
    << " (" << m_DeleteStack.top().m_iLine << ")" << std::endl;
  m_Blocks.erase( it );
  m_DeleteStack.pop();
  free( pPtr );
}

} // end namespace